{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dcf0d13a",
   "metadata": {},
   "source": [
    "## 4.3 分类问题\n",
    "\n",
    "要说人类最基本的智能形态，一种就是前面讲过的回归，另一种就是分类了，这也是为什么机器学习把它们当成研究重点的原因。想想我们小时候，困了睡饿了吃，这种跟习惯或者说顺序相关的就是回归，它研究的是数据内在的“惯性”规律，一旦找到了可以用来预测；同时，还有一种智能，不断学习这是啥，那是啥，这种智能的形式就是分类任务。\n",
    "\n",
    "机器学习把人类的这种能力进行了抽象。分类问题可以说是一类常见的监督学习问题。它涉及将输入数据集划分到一个或多个类别中的过程。通常，这些类别是事先确定的，并且类别标签是已知的。例如，在一个垃圾邮件过滤器的分类问题中，输入数据可能是电子邮件，而输出类别可能是“垃圾邮件”或“非垃圾邮件”。在这种情况下，目标是使用机器学习算法训练模型，使其能够根据电子邮件的内容将其分类为“垃圾邮件”或“非垃圾邮件”。分类问题的模型的输出是一个离散的类别标签，而不是连续的值。这是和回归问题非常大的区别。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afa5e0f9",
   "metadata": {},
   "source": [
    "### 4.3.1 多分类问题的数学表示\n",
    "机器学习算法的套路往往都是先用一个数学模型描述一个问题，然后找到一个目标函数，再用最优化的方法逼近求解得到模型参数。这个过程就是机器学习，也就是训练的过程。先来看看怎么把这样一个带有离散类别标签的分类问题用数学的方法来表示。\n",
    "\n",
    "在数学表示中，我们通常使用一个向量来表示输入数据。这个向量通常被称为“特征向量”，并且由输入数据的多个特征构成。例如，如果我们想要对图像进行分类，我们可能会使用像素值作为特征。怎么表示离散的类别标签呢？一种常见的表示方法是使用“One-hot 编码”。这种方法将每个可能的类别映射到一个独立的维度上，并在该维度上使用“1”来表示类别。例如，如果我们有三个可能的类别，分别是“猫”、“狗”和“鸟”，则可以使用如下的 One-hot 编码：\n",
    "\n",
    "猫：[1, 0, 0]\n",
    "狗：[0, 1, 0]\n",
    "鸟：[0, 0, 1]\n",
    "\n",
    "这种表示方法的优点在于，它可以很容易地区分不同的类别，并且可以通过使用线性模型来进行分类。然而，One-hot 编码也有一些缺点，例如当我们的类别数量很大时，它会导致特征维度数量变得非常大。另一种常用的方法是使用“概率表示”。在这种方法中，我们会使用每个类别的概率来表示输入数据属于该类别的可能性。例如，如果我们想要分类一张图像，可以使用概率表示如下：\n",
    "\n",
    "猫：0.7\n",
    "狗：0.2\n",
    "鸟：0.1\n",
    "\n",
    "这种方法的优点在于，它可以很容易地表示输入数据属于不同类别的可能性，并且可以使用贝叶斯公式进行分类。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1659960b",
   "metadata": {},
   "source": [
    "<div>\n",
    "    <img src=\"../images/logo.jpg\" alt=\"Drawing\" style=\"width: 80px;\" align=\"left\"/>\n",
    "    <div>\n",
    "        <h4>深入思考和进阶学习 ：</h4>\n",
    "人工智能中的分类其实是个很大的话题，严格意义上会分成机器学习的分类问题、深度学习的分类问题、注意力机制下的分类问题等等。从原理上看，又分为二分类、多分类和多标签分类等问题。如果你对这方面的内容感兴趣，想一口气彻底搞明白，欢迎选修我们的<a href=\"https://appmixy0usl5902.h5.xiaoeknow.com/\">专题课程</a>\n",
    "    </div>\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97b29659",
   "metadata": {},
   "source": [
    "### 4.3.2 线性模型和Softmax回归\n",
    "Softmax回归，也被称为多项式逻辑回归，可以输出多个类别的概率。用数学的语言来说，\n",
    "设输入图像的向量表示为$\\mathbf{x}$，模型可以写成如下形式：\n",
    "$$\\hat{y} = \\text{softmax}(\\mathbf{W}\\mathbf{x} + \\mathbf{b})$$\n",
    "其中$\\mathbf{W}$和$\\mathbf{b}$是模型参数，也是要学习的变量。$\\mathbf{W}$是个矩阵，其中的每一列对应一个特征，每一行对应一个类别。$\\mathbf{b}$是一个向量，其中的每一个元素对应一个类别。$\\hat{y}$是模型输出，它是一个长度为$K$的向量，$K$是类别的数量。每一个元素$\\hat{y}_i$表示输入图像属于$i$个类别的概率。我们希望每个元素$\\hat{y}_i$越大越好，因为这意味着输入图像属于第$i$个类别的可能性越大。\n",
    "$\\text{softmax}(\\cdot)$是一个函数，它可以将向量$\\mathbf{W}\\mathbf{x} + \\mathbf{b}$中的每一个元素转换为概率值。具体来说，我们可以使用如下式子：\n",
    "$$\\text{softmax}(\\mathbf{z})_i = \\frac{\\exp(z_i)}{\\sum_{j=1}^K \\exp(z_j)}$$\n",
    "其中$z_i$表示中第$i$个元素的值。这个函数的作用是将每一个元素转换为概率值，使得所有的概率值之和为$1$。\n",
    "在多分类问题中使用 Softmax 运算的好处是因为它可以将输入的特征向量转换为概率值，这个概率值更加符合我们的直觉，从而方便进行决策。\n",
    "数学模型有了，按照我们上节课的套路，下面要确定一个目标函数，也就是让数据投票。在多项式逻辑回归中，这就是交叉熵损失函数了。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2d03a3a",
   "metadata": {},
   "source": [
    "### 4.3.3 损失函数\n",
    "你可能会问了，为啥不继续使用线性回归中的均方误差做损失函数呢？\n",
    "\n",
    "在多项式逻辑回归中，我们希望模型能够准确地预测输出值。但是，多项式逻辑回归是一种二元分类模型，它的输出值是一个概率值，而不是一个连续的实数值。因此，使用均方误差作为损失函数就不太合适了，它虽然能衡量回归预测结果与真实值之间差距，但是不能很好地衡量分类结果与真实结果之差。\n",
    "\n",
    "举个例子，假设有一个二元分类模型，它的输出是一个概率值，表示样本属于正类的概率。如果使用均方误差作为损失函数，则对于一个样本，如果真实结果是正类，而模型预测的概率是$2.0$，则均方误差的值为$(1 - 2.0)^2 = 1.0$。如果真实结果是正类，而模型预测的概率是$-0.5$，则均方误差的值为$(1 - (-0.5))^2 = 1.25$。可以看到，在这种情况下，均方误差并不能很好地考虑概率值的范围限制。因此，在多项式逻辑回归中，通常不使用均方误差作为损失函数。相反，会选择使用对数似然函数或者交叉熵作为损失函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f47009f8",
   "metadata": {},
   "source": [
    "#### 1. 对数损失函数\n",
    "对数损失函数，也称为对数似然损失函数或者对数似然函数，是深度学习中一种常用的损失函数。它衡量的是在给定的观测数据的情况下，模型的参数的最优取值。当模型的参数取得最优时，对数损失函数也取得最小值。\n",
    "\n",
    "对数损失函数的公式定义如下：\n",
    "\n",
    "对于二分类问题，对数损失函数为：\n",
    "\n",
    "$$L(y, f(x)) = - [y  log(f(x)) + (1 - y) log(1 - f(x))]$$\n",
    "\n",
    "对于多分类问题，对数损失函数为：\n",
    "\n",
    "$$L(y, f(x)) = - ∑_iy_i log f(x_i)$$\n",
    "\n",
    "其中，$y$表示真实的标签，$f(x)$表示模型的预测结果，$i$表示类别的编号。\n",
    "\n",
    "为什么要用对数运算呢？这是因为对数运算具有很多优秀的性质。例如，对数运算是单调的，也就是说，当一个数越大，它的对数值也越大；对数运算具有结合性，也就是说，对数运算可以将多个数的乘积转化为多个数的和的形式；对数运算还具有放缩性，也就是说，对数运算可以将一个大范围的数值压缩到一个小范围的数值中。这些性质使得对数损失函数在优化模型参数时具有很大的优势。例如，当模型的参数取得最优时，对数损失函数也取得最小值，这使得我们可以直接使用梯度下降等优化算法来最小化对数损失函数。\n",
    "\n",
    "上面的公式具体怎么来的呢？如果感兴趣的话，我们可以从概率的角度推导一下。假设我们有一个二分类问题，其中y=1表示正类，y=0表示负类。那么对数损失函数可以表示为：\n",
    "$$L(y, f(x)) = - [y logP(y=1|x) + (1 - y) logP(y=0|x)]$$\n",
    "其中$P(y=1|x)$表示在给定观测数据$x$的情况下，标签为$1$的概率，$P(y=0|x)$表示在给定观测数据$x$的情况下，标签为$0$的概率。由于概率的性质，我们可以得到：\n",
    "$$P(y=1|x) + P(y=0|x) = 1$$\n",
    "那么对数损失函数就可以简化为：\n",
    "$$L(y, f(x)) = - [y  log(f(x)) + (1 - y) log(1 - f(x))]$$\n",
    "这就是对数损失函数的公式定义。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28f2716f",
   "metadata": {},
   "source": [
    "#### 2. 交叉熵损失函数\n",
    "在使用对数损失函数时，通常会使用softmax函数把模型的预测结果转化为概率分布的形式。我们前面给出了它的公式，这里再强化一下：\n",
    "$$f(x) = \\frac{e^x} {∑_i(e^x_i)}$$\n",
    "\n",
    "其中，$x$表示模型的预测结果，$i$表示类别的编号。例如，在二分类问题中，Softmax函数的输出就是一个长度为2的数组，分别表示正类和负类的概率。\n",
    "\n",
    "使用Softmax函数之后，我们就可以将对数损失函数的公式定义应用到多分类问题中。这就是交叉熵损失函数了，它的公式长这样\n",
    "$$L(y,f(x))=\\frac{1}{m}\\sum\\limits^m_{i=1}\\sum\\limits^n_{j=1}y_{ij}\\log p(x_{ij})$$\n",
    "\n",
    "\n",
    "其中，$m$表示样本数，$n$表示样本所属不同的类别个数，$y_{ij}$表示样本$i$所属类别$j$,$p(x_{ij})$表示预测的样本$i$属于类别$j$的概率。\n",
    "\n",
    "从上述的表达式中看，两者的损失函数本质是一样的，但是这里需要注意的是通常情况下，这两种损失函数所对应的上一层结构不同，log loss经常对应的是Sigmoid函数的输出，用于二分类问题；而交叉熵经常对应的是Softmax函数的输出，用于多分类问题。神经网络中经常使用交叉熵作为评判参数优化的函数，而在二分类的场景下经常使用对数损失函数作为评判参数优化的函数。但是其实对数损失也可以应用在多分类，这时候就和交叉熵应用在多分类没有什么差别了。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "278b1d13",
   "metadata": {},
   "source": [
    "### 4.3.4 熵、相对熵、交叉熵\n",
    "在深度学习中我们会反复提到熵、相对熵、交叉熵的概念，很多同学可能没接触过不熟悉，还有的同学可能学过但是忘记了。咱们在这里再复习一下。\n",
    "\n",
    "我们现在说的熵是信息论中的一个重要概念。熵是用来衡量随机变量不确定性的度量。它表示随机变量所有可能取值的概率分布的期望信息量。熵的公式为：\n",
    "$$H(X)=-∑p(x)logp(x)$$\n",
    "其中，$X$表示随机变量，$p(x)$表示随机变量$X$取值$x$的概率。$logp(x)$表示以$2$为底的对数，这是为了使熵的单位为“位”。\n",
    "\n",
    "熵的应用非常广泛，它可以用来衡量信息的有效性、信息的安全性、信息的可靠性等。例如，在信息安全领域，熵可以用来衡量密码强度；在信息压缩领域，熵可以用来衡量信息的有效性；在通信领域，熵可以用来衡量信息的不对称性。\n",
    "\n",
    "在深度学习中，熵可以用来衡量模型的复杂度。模型的复杂度越大，说明模型的表示能力就越强，但同时也意味着模型可能出现过拟合的情况。而熵的值越小，说明模型的复杂度越低，模型可能出现欠拟合的情况。因此，在训练深度学习模型时，可以使用熵来衡量模型的复杂度，从而调整模型的复杂度，使得模型在训练过程中达到最佳的表现。\n",
    "\n",
    "交叉熵和相对熵是两个常用的损失函数，它们都可以用来衡量模型预测的输出与真实标签之间的差异。交叉熵损失函数的定义我们都很熟悉了，如下：\n",
    "\n",
    "$$ H(p, q) = -\\sum_{x} p(x) \\log q(x) $$\n",
    "\n",
    "其中 $p$ 是真实的概率分布，$q$ 是模型预测的概率分布，$x$ 是输出的类别。\n",
    "\n",
    "交叉熵损失函数可以用来衡量两个概率分布之间的差异。如果两个分布接近，那么交叉熵损失函数的值就会较小；如果两个分布差异较大，那么交叉熵损失函数的值就会较大。\n",
    "\n",
    "相对熵是另一种常用的损失函数，它也可以用来衡量预测输出与真实标签之间的差异。它的定义如下：\n",
    "\n",
    "$$ D_{KL}(p || q) = \\sum_{x} p(x) \\log \\frac{p(x)}{q(x)} $$\n",
    "\n",
    "其中 $p$ 是真实的概率分布，$q$ 是模型预测的概率分布，$x$ 是输出的类别。\n",
    "\n",
    "相对熵也可以用来衡量两个概率分布之间的差异。如果两个分布接近，那么相对熵的值就会较小；如果两个分布差异较大，那么相对熵的值就会较大。\n",
    "\n",
    "交叉熵和相对熵都可以用来衡量两个概率分布之间的差异，但是它们之间有一些区别。\n",
    "\n",
    "首先，交叉熵是一种非对称的损失函数，它的值只和真实概率分布 $p$ 有关。相对熵是一种对称的损失函数，它的值同时取决于真实概率分布 $p$ 和预测概率分布 $q$。\n",
    "\n",
    "其次，交叉熵损失函数可以用来衡量两个概率分布之间的差异，但是它不能直接用来比较两个概率分布的相似度。相对熵损失函数可以直接用来比较两个概率分布的相似度。\n",
    "\n",
    "最后，交叉熵损失函数更常用于分类问题，因为它可以用来衡量模型对于不同类别的预测准确度。相对熵损失函数更常用于估计概率分布，因为它可以用来衡量模型预测的概率分布与真实概率分布之间的差异。\n",
    "\n",
    "这些内容一上来对你可能有点深，暂时听不懂没有关系，随着学习的深入会逐渐的理解。为了知识结构的系统性和完整性，我们在这里一并先列出了，主要也是考虑到同学们的情况各不相同，很多已经有一定基础，但是概念的掌握也许还不是那么到位。因此，你也可以把它当成是一个词典或者工具书，在概念忘记时，能到这里来复习。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "199aa9e1",
   "metadata": {},
   "source": [
    "<div>\n",
    "    <img src=\"../images/logo.jpg\" alt=\"Drawing\" style=\"width: 80px;\" align=\"left\"/>\n",
    "    <div>\n",
    "    <h4>深入思考和进阶学习 ：</h4>\n",
    "学好机器学习和深度学习，除了交叉熵、相对熵之外，还要深入掌握信息熵、联合熵、条件熵、互信息、信息增益等概念之间的联系与区别，能不能用一张图说清楚？此外熵的本质是什么？这么多的数学概念、公式记不住怎么办？它们与决策树、神经网络等各种算法间的关系是怎么样的。如果你被这些问题困扰已久，欢迎选修梗直哥的<a href=\"https://appmixy0usl5902.h5.xiaoeknow.com/\">进阶课程</a>，生动的例子，各种动画给你讲明白。\n",
    "    </div>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9fcbd23",
   "metadata": {},
   "source": [
    "[Next 4-4线性回归代码实现](./4-4%20线性回归代码实现.ipynb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "abcfc1d7",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
